package com.ichifinga.airwave.shared;

import com.ichifinga.airwave.shared.enums.Colours;
import com.ichifinga.airwave.shared.entities.Phrase;
import com.ichifinga.airwave.shared.enums.FlashTypes;
import com.ichifinga.airwave.shared.enums.ScrollTypes;
import com.ichifinga.airwave.shared.enums.ShadeTypes;
import com.ichifinga.airwave.shared.enums.TextShades;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Typeface;

public class DisplayObject
{	
	private Phrase _phrase;
	private Paint _paint;
	private Bitmap _bitmap;
	
	private String[] _wordsToDisplay;
	
	private float _screenWidth;
	private float _screenHeight;
	private float _maxTextHeight;
	private float _actualTextHeight;
	private float _actualMillisecondsPerLetter;
	private float _textBitmapHeight;
	private float _textBitmapWidth;
	private float _yDisplacement;
	private float _maxScrollWidth;
	private float _periodInMilliseconds;
	private float _averageLetterWidth;
		
	private long _startTime;
	private long _timeElapsed;
	private int _currentFrame = 0;
	
	private long _lastFrameTime = 0;
	private float _frameSpeed = 0;

	float MillisecondsPerLetter = 300f;
	float MillisecondsPerFrame = 1000f;
	
	public DisplayObject(boolean editMode, int width, int height, Phrase phrase)
	{	
		_screenWidth = width;
		_screenHeight = height;
				
		_phrase = phrase;
		
        _paint = new Paint();
    	_paint.setTypeface(Typeface.create("sans", Typeface.BOLD));
    	
    	_wordsToDisplay = _phrase._textToDisplay.split(" ");
    	
    	DoOnceOnlyCalculationsForCurrentOrientation();
	}
	
	public void DoOnceOnlyCalculationsForCurrentOrientation()
	{
		OrientationInitialise();
		
		CalculateTextSize();
		
		SetupShader();
		
		CreateTextBitmap();	
		
		if(_phrase._scrollType == ScrollTypes.Bounce || _phrase._scrollType == ScrollTypes.Scroll)
		{
			CalculateScrollSpeed();
			CalculatePeriod();
		}
		else if(_phrase._scrollType == ScrollTypes.SequenceTimed)
		{
			CalculateFrameSpeed();
		}
			
		CalculateVerticalPosition();
		
		_startTime = System.currentTimeMillis();
		_lastFrameTime =_startTime; 
	}
		
	public void OrientationInitialise()
	{
		_maxTextHeight = (float)Math.min(_screenWidth, _screenHeight);
	}

	private void CalculateTextSize()
	{
		switch(_phrase._textWidth)
		{
			case Thin :
				_paint.setTextScaleX(.75f);
				break;
				
			case Normal :
				_paint.setTextScaleX(1f);
				break;
				
			case Thick :
				_paint.setTextScaleX(1.5f);
				break;
		}
				
		if(_phrase._scrollType == ScrollTypes.Bounce || _phrase._scrollType == ScrollTypes.Scroll)
		{	
	    	switch (_phrase._textSize)
	    	{
		    	case Large:
		    		_actualTextHeight = _maxTextHeight;
		    		break;
		    	case Medium:
		    		_actualTextHeight = _maxTextHeight * 0.75f;
		    		break;
		    	case Small:
		    		_actualTextHeight = _maxTextHeight * 0.5f;
		    		break;    	
	    	}
	    	
	    	_paint.setTextSize(_actualTextHeight);
		}
		else
		{	
			_paint.setTextSize(_maxTextHeight);
			Rect bounds = new Rect();
			
			if(_phrase._scrollType == ScrollTypes.Fit)
			{
				_paint.getTextBounds(_phrase._textToDisplay,0, _phrase._textToDisplay.length(), bounds);
			}
			else //Sequence
			{
				_paint.getTextBounds(_wordsToDisplay[_currentFrame],0, _wordsToDisplay[_currentFrame].length(), bounds);
			}
			
			_actualTextHeight = _maxTextHeight *
							Math.min(_screenWidth / (float)bounds.width(), _screenHeight / (float)bounds.height());
			_paint.setTextSize(_actualTextHeight);
		}
	}
		
	public void SetupShader()
	{
		if(_phrase._flashType == FlashTypes.Police)
		{
			_paint.setColor(Colours.White.getAARRGGBB());
		}
		else if(_phrase._textShade == TextShades.OneColour)
    	{
    		_paint.setColor(_phrase._colourOne.getAARRGGBB());
    	}
    	else
    	{
    		int[] colours = null;
    		
	    	switch (_phrase._textShade)
	    	{
	    		case TwoColour:
	    			colours = new int[2];
	    	    	colours[0] = _phrase._colourOne.getAARRGGBB();
	    	    	colours[1] = _phrase._colourTwo.getAARRGGBB();
	    			break;
	    			
	    		case ThreeColour:
	    			colours = new int[3];
	    	    	colours[0] = _phrase._colourOne.getAARRGGBB();
	    	    	colours[1] = _phrase._colourTwo.getAARRGGBB();
	    	    	colours[2] = _phrase._colourThree.getAARRGGBB();
	    			break;
	    			
	    		case Rainbow:
	    			colours = new int[7];
	    	    	colours[0] = Colours.Red.getAARRGGBB();
	    	    	colours[1] = Colours.Orange.getAARRGGBB();
	    	    	colours[2] = Colours.Yellow.getAARRGGBB();
	    	    	colours[3] = Colours.Green.getAARRGGBB();
	    	    	colours[4] = Colours.Blue.getAARRGGBB();
	    	    	colours[5] = Colours.Indigo.getAARRGGBB();
	    	    	colours[6] = Colours.Violet.getAARRGGBB();
	    			break;
	    			
	    		case Chill:
	    			colours = new int[3];
	    			colours[0] = Colours.Green.getAARRGGBB();
	    	    	colours[1] = Colours.Orange.getAARRGGBB();
	    	    	colours[2] = Colours.Yellow.getAARRGGBB();
	    			break;
	    			
	    		case Fire:
	    			colours = new int[4];
	    			colours[0] = Colours.Red.getAARRGGBB();
	    	    	colours[1] = Colours.Orange.getAARRGGBB();
	    	    	colours[2] = Colours.Yellow.getAARRGGBB();
	    	    	colours[3] = Colours.White.getAARRGGBB();
	    			break;
	    			
	    		case Ice:
	    			colours = new int[3];
	    			colours[0] = Colours.Blue.getAARRGGBB();
	    	    	colours[1] = Colours.Cyan.getAARRGGBB();
	    	    	colours[2] = Colours.White.getAARRGGBB();
	    			break;
	    			
	    		case Olympic:
	    			colours = new int[5];
	    	    	colours[0] = Colours.Blue.getAARRGGBB();
	    	    	colours[1] = Colours.Yellow.getAARRGGBB();
	    	    	colours[2] = Colours.Black.getAARRGGBB();
	    	    	colours[3] = Colours.Green.getAARRGGBB();
	    	    	colours[4] = Colours.Red.getAARRGGBB();	    	    	
	    			break;
	    	}
	    	
	    	float[] shadeLimits = null;
	    	if(_phrase._shadeType == ShadeTypes.Block)
	    	{
	    		int[] newColours = new int[colours.length * 2];
	    		
	    		shadeLimits = new float[colours.length * 2];
	    		for(int counter = 0; counter < colours.length; counter ++)
	    		{
	    			shadeLimits[counter * 2] = (float)counter / (float)colours.length;
	    			shadeLimits[counter * 2 + 1] = (float)(counter + 1) / (float)colours.length;
	    			
	    			newColours[counter * 2] = colours[counter];
	    			newColours[counter * 2 + 1] = colours[counter];
	    		}
	    		
	    		colours = newColours;
	    	}
	    	
	    	////////////////////////////////////////////////////
	    	//force upper case...for now - remove the descent...
    		////////////////////////////////////////////////////
	    	
	    	FontMetrics fm = _paint.getFontMetrics();
	    	float capsctualTextHeight =_actualTextHeight - fm.descent;
	    	
	    	Shader shader = new LinearGradient(0f, 0f, 0f, capsctualTextHeight, colours, shadeLimits, Shader.TileMode.CLAMP);
				    	
	    	_paint.setShader(shader);
    	}
	}
	
	public void CreateTextBitmap()
	{		
		Rect bounds = new Rect();
		
		String actualTextToDisplay = 
			_phrase._scrollType == ScrollTypes.Sequence || _phrase._scrollType == ScrollTypes.SequenceTimed
			? _wordsToDisplay[_currentFrame]
			: _phrase._textToDisplay;
		
		_paint.getTextBounds(actualTextToDisplay,0, actualTextToDisplay.length(), bounds);
				
		_textBitmapHeight = bounds.height(); 
		_textBitmapWidth = bounds.width();
		
		_bitmap = Bitmap.createBitmap((int)_textBitmapWidth, (int)_textBitmapHeight, Bitmap.Config.RGB_565);
		
		Canvas createCanvas = new Canvas();
		createCanvas.setBitmap(_bitmap);
		_paint.setAlpha(255);
		createCanvas.drawColor(
				_phrase._flashType == FlashTypes.Police
    			? 0xff000000
    			: _phrase._backgroundColour.getAARRGGBB());
				
		createCanvas.drawText(actualTextToDisplay, - bounds.left, 
				_textBitmapHeight - bounds.bottom, _paint);
		_averageLetterWidth = _textBitmapWidth / (float)actualTextToDisplay.length();
	}
	
	public void CalculateScrollSpeed()
	{	
    	switch (_phrase._displaySpeed)
    	{
	    	case Fast:
	    		_actualMillisecondsPerLetter = MillisecondsPerLetter * 0.5f;
	    		break;
	    	case FastIsh:
	    		_actualMillisecondsPerLetter = MillisecondsPerLetter * 0.75f;
	    		break;
	    	case Normal:
	    		_actualMillisecondsPerLetter = MillisecondsPerLetter;
	    		break;
	    	case SlowIsh:
	    		_actualMillisecondsPerLetter = MillisecondsPerLetter * 1.5f;
	    		break;
	    	case Slow:
	    		_actualMillisecondsPerLetter = MillisecondsPerLetter * 2f;
	    		break;    	
    	}	
	}
	
	public void CalculateFrameSpeed()
	{	
    	switch (_phrase._displaySpeed)
    	{
	    	case Fast:
	    		_frameSpeed = MillisecondsPerFrame * 0.5f;
	    		break;
	    	case FastIsh:
	    		_frameSpeed = MillisecondsPerFrame * 0.75f;
	    		break;
	    	case Normal:
	    		_frameSpeed = MillisecondsPerFrame;
	    		break;
	    	case SlowIsh:
	    		_frameSpeed = MillisecondsPerFrame * 1.5f;
	    		break;
	    	case Slow:
	    		_frameSpeed = MillisecondsPerFrame * 2f;
	    		break;    	
    	}	
	}
	
	public void CalculatePeriod()
	{
		if(_phrase._scrollType == ScrollTypes.Bounce){
			_maxScrollWidth = _textBitmapWidth;
		}
		else if(_phrase._scrollType == ScrollTypes.Scroll)
		{
			_maxScrollWidth = _screenWidth + _textBitmapWidth;
		}
    	_periodInMilliseconds = _maxScrollWidth / _averageLetterWidth * _actualMillisecondsPerLetter; 
    }
	
	public void CalculateVerticalPosition()
	{
		_yDisplacement = (_screenHeight - _textBitmapHeight) / 2;
	}
	
	public void SetAlpha()
	{	
    	if(_phrase._flashType != FlashTypes.None)
    	{
	    	int alpha = 255;
	    	
	    	switch(_phrase._flashType)
	    	{	    	
		    	case Flash:
		    		alpha = _timeElapsed % 500 > 250 ? 0 : 255;
		    		break;
		    	
		    	case Strobe:
		    		alpha = _timeElapsed % 200 > 100 ? 0 : 255;
		    		break;
		    	
		    	case Pulse:		    		
		    		alpha = (int)((Math.cos(_timeElapsed % 1000f / 1000f * 2f * Math.PI) + 1f) / 2f * 255f);
		    		break;
		    		
		    	case Police:		    		
		    		alpha = (int)((Math.cos(_timeElapsed % 500f / 500f * 2f * Math.PI) + 1f) / 2f * 255f);
		    		break;
	    	}
	    	_paint.setAlpha(alpha);
    	}
	}
		
	public void Draw(Canvas canvas)
	{
		long currentTime = System.currentTimeMillis();
    	_timeElapsed = currentTime - _startTime;
    	
    	canvas.drawColor(
    			_phrase._flashType == FlashTypes.Police
    			? 0xff000000
    			: _phrase._backgroundColour.getAARRGGBB());
    	
    	SetAlpha();
    	
    	if(_phrase._scrollType == ScrollTypes.Fit || _phrase._scrollType == ScrollTypes.Sequence || _phrase._scrollType == ScrollTypes.SequenceTimed)
    	{    	
    		if(_phrase._scrollType == ScrollTypes.SequenceTimed)
    		{
    			if(currentTime -_lastFrameTime > _frameSpeed )
    			{
    				_lastFrameTime = currentTime;
    				NextFrame();
    			}
    		}
    		
    		canvas.drawBitmap(_bitmap, (_screenWidth - _textBitmapWidth) /2, _yDisplacement, _paint);
    		
    		if(_phrase._flashType == FlashTypes.Police)
    		{
    			int currentFlashColour = _timeElapsed % 1000f > 250f &&_timeElapsed % 1000f < 750f
    				? 0xffff2222
    				: 0xff2222ff;
				canvas.drawColor(currentFlashColour, Mode.MULTIPLY);
    		}
    		
    		return;
    	}
    	
    	float modTimeElapsed = _timeElapsed % _periodInMilliseconds;///this
    	
    	if(_phrase._scrollType == ScrollTypes.Bounce)
    	{
    		///this
    		if(_timeElapsed % (2 * _periodInMilliseconds) >  _periodInMilliseconds)
    		{
    			modTimeElapsed = _periodInMilliseconds - modTimeElapsed;
    		}
    	}
    	
    	float xDisplacement = _screenWidth - modTimeElapsed / _periodInMilliseconds * _maxScrollWidth;
    	
		xDisplacement = modTimeElapsed / _periodInMilliseconds * _maxScrollWidth;
    	
    	Rect src = new Rect();
    	src.top = 0;
    	src.bottom = (int)_textBitmapHeight;
    	Rect dst = new Rect();
    	dst.top = (int) _yDisplacement;
    	dst.bottom = (int)(_yDisplacement + _textBitmapHeight);
    	
    	if(_phrase._scrollType == ScrollTypes.Scroll)
    	{
	    	if(xDisplacement < _screenWidth)
	    	{
	    		src.left = 0;
	    		src.right = (int)xDisplacement;
	    		dst.left = (int)(_screenWidth - xDisplacement);
	    		dst.right = (int)(_screenWidth);
	    	}
	    	else if(xDisplacement < _maxScrollWidth - _screenWidth)
	    	{
	    		src.left = (int)(xDisplacement - _screenWidth);
	    		src.right = (int)(xDisplacement);
	    		dst.left = 0;
	    		dst.right = (int)(_screenWidth);
	    	}
	    	else
	    	{    	
	    		src.left = (int)(xDisplacement - _screenWidth);
	    		src.right = (int)_textBitmapWidth;
	    		dst.left = 0;
	    		dst.right = (int)(_maxScrollWidth - xDisplacement);
	    	}
    	}
    	else if(_phrase._scrollType == ScrollTypes.Bounce)
    	{
    		if(xDisplacement < _screenWidth)
	    	{
	    		src.left = 0;
	    		src.right = (int)(xDisplacement + _screenWidth / 2f);
	    		dst.left = (int)(_screenWidth / 2f - xDisplacement);
	    		dst.right = (int)(_screenWidth);
	    	}
	    	else if(xDisplacement < _maxScrollWidth - _screenWidth / 2f)
	    	{
	    		src.left = (int)(xDisplacement - _screenWidth / 2f);
	    		src.right = (int)(xDisplacement + _screenWidth / 2f);
	    		dst.left = 0;
	    		dst.right = (int)(_screenWidth);
	    	}
	    	else
	    	{    	
	    		src.left = (int)(xDisplacement - _screenWidth / 2);
	    		src.right = (int)_textBitmapWidth;
	    		dst.left = 0;
	    		dst.right = (int)(_maxScrollWidth - xDisplacement + _screenWidth / 2);
	    	}
    	}
   			   	
		canvas.drawBitmap(_bitmap, src , dst, _paint);
		
		if(_phrase._flashType == FlashTypes.Police)
		{
			int currentFlashColour = _timeElapsed % 1000f > 250f &&_timeElapsed % 1000f < 750f
				? 0xffff2222
				: 0xff2222ff;
			canvas.drawColor(currentFlashColour, Mode.MULTIPLY);
		}
	}

	public void NextFrame()
	{
		if(_phrase._scrollType == ScrollTypes.Sequence || _phrase._scrollType == ScrollTypes.SequenceTimed)
		{
			_currentFrame  = 
				_currentFrame < _wordsToDisplay.length - 1
				? _currentFrame + 1
				: 0;
			
			DoOnceOnlyCalculationsForCurrentOrientation();
		}
	}
	
	public void PreviousFrame()
	{
		if(_phrase._scrollType == ScrollTypes.Sequence || _phrase._scrollType == ScrollTypes.SequenceTimed)
		{
			_currentFrame  =
				_currentFrame == 0
				? _wordsToDisplay.length - 1
				: _currentFrame - 1;
			
			DoOnceOnlyCalculationsForCurrentOrientation();
		}
	}
	
	public void FirstFrame()
	{
		if(_phrase._scrollType == ScrollTypes.Sequence || _phrase._scrollType == ScrollTypes.SequenceTimed)
		{
			_currentFrame  = 0;
			
			DoOnceOnlyCalculationsForCurrentOrientation();
		}
		else
		{
			_startTime = System.currentTimeMillis();
		}
	}
}
